#ifndef CORRELATION_H
#define CORRELATION_H

#include <iostream>
#include <fstream>

#include <opencv/cv.h>
#include <opencv/cvaux.h>
#include <opencv/highgui.h>
#include <opencv/cxcore.h>
#include <opencv/cvvidsurv.hpp>

#include "/home/administrator/Documents/correlate/Image.h"


using namespace std;

// Constants used for static array sizing
const int max_num_blobs = 1000;
const int max_neighbor_count = 36;  // Allow 10-degree increments
const int calculation_count = 3;


/*
  Correlation class

  This class provides functions which can store and analyze an image for the digital image correlation procedure. The
  class is meant to be used in the context of a sequence of images taken by a camera in an inertial reference frame.

  Note: Dynamic memory allocation allows for larger arrays. This is because the stack has more memory available than
  the heap.

  Rule: cvPoint(x, y)
     x -> column
     y -> row

  Rule: bw[r][c]
     r -> row
     c -> column

  */
class correlation {

public:

    /* ----- Class Implementation Functions ------------------------------------------------------------------------- */

    correlation();


    /* ----- Level 0 Functions -------------------------------------------------------------------------------------- */

    // Re-initialize the object, in preparation for processing an image sequence
    void initObject();

    // Inputs a specified image file, and outputs a set of point positions
    void analyzeImage(string destPathName, string sourcePathName, string fileName, bool makeReferenceImage);

    // Saves to a file the entire contents of the positionField_ref or positionField array (which depends on the bool)
    void storePositionField(string path, bool isFirstRun=false);

    // Save the current strain field to a file
    void storeStrainField(string path, bool isFirstRun=false);

    // Output the current image to a window with useful annotation - b/w image, bounding boxes
    void outputImage(bool showInWindow=true);

    // Output visualization of the strain field in which the lightness component is altered to reflect strain intensity
    void outputOverlay(bool showInWindow=true);


    /* ----- Level 1 Functions -------------------------------------------------------------------------------------- */

    // Load and save parameters which remain persistent for all objects
    void loadPersistentValues();
    void savePersistentValues();

    // Inputs image file and outputs a black-and-white (b/w) image
    void inputImage(string file);

    // Populate the core blob analysis arrays by analyzing contiguous regions of true pixels in b/w image
    void blobAnalysis();

    // Inputs core blob analysis arrays; outputs position field array
    void extractPositionField(bool isFirstRun=false);

    // Populate gridMap, which matches each dot with up to max_neighbor_count nearby dots
    void generateGridMap();

    // Calculate the 2D strain tensor components at each dot location (assuming locally bilinear displacemnt field)
    void computeStrain();

    // Alters imgProc to show the current state of blob analysis - which blobs have been identified
    void drawCurrentState(bool showInWindow=false);


    /* ----- Level 2 Functions -------------------------------------------------------------------------------------- */

    // Subset the imgInFull image, resulting in imgIn
    RgbImage subsetImage(RgbImage src);

    // Threshold current image to obtain b/w image
    void filterImage(IplImage *imgInLocal);

    // If the pixel at (row,column)=(r,c) is touching a blob, return the blob index b; else return -1
    int matchBlob(int r, int c, int criteria);

    // For the current and new pixel, expand the bounding box
    void updateBoundingBox(int blob_track, int r, int c);

    // Merge information from fromBlob to toBlob; delete information for fromBlob
    void mergeBlobs(int fromBlob, int toBlob);

    // Corrector step for blob analsys; compute quantities which require blob analysis to be finished; assign blob
    //   probabilities
    void postProcess();

    //  Determine the probability that each blob is a dot
    void blobProbability();

    // Based on probabilities, identify which blobs are actually dots
    void identifyDots();

    // Calculate the angle between vectors in the reference and deformed configurations; check against
    //   min_direction_angle
    bool isValidPointSet(double pt[2][3], double pt_star[2][3], int p, int n1, int n2);

    // Compute the strain at pt[][0], using the specified approximation method
    void localStrain(double pt[2][3], double pt_star[2][3], int count, int method);

    // Return min(a,b), but never return -1
    double minIgnore(double a, double b);

    // Return max(a,b), but never return -1
    double maxIgnore(double a, double b);

    // Draw the requested property for the requested blob in imgProc
    void drawBlobProperty(int whichProperty, int blob_track, int B, int G, int R);

    // Draw a marker at the pixel (r,c) so the point can be easily picked out
    void drawCurrentPixel(int r, int c, int B, int G, int R);

    //  Print information about internal functions for debugging purposes
    void debugHandle(int direction=-1, string functionName="unknown function");


public:


    // Object re-initialization parameters
    string persistFileString;

    // Runtime logic
    int enable_debug;  // Call function with --enable-debug to output spiffy stuff during analysis
    bool enable_step;  // Pause after outputting image every time step
    int image_sequence;  // Flag -is=### runs with pre-stored image sequence number ###

    // Image data
    int h, w;  // Discovered image dimensions
    int colorSpace;
    int colorSpacePrev;
    CvRect ROI;  // Image ROI

    // Images
    //   Note: See Image.h for RgbImage definition. Note this definition includes IplImage pointer
    RgbImage imgInFull;  // Complete input image; this is the only IplImage with "large" size
    RgbImage imgIn;  // Subset of input image; all images except imgInFull have the same size
    BwImage bw;  // Single-channel IPL_DEPTH_8U, black-and-white version of imgIn
    RgbImage imgProc;  // Three-channel image after processing

    // Image filter logic variables
    int R_lower, R_upper, G_lower, G_upper, B_lower, B_upper;
    int H_lower, H_upper, S_lower, S_upper, V_lower, V_upper;

    // Blob analysis logic variables
    int num_blobs;
    int found;
    double min_blob_area, max_blob_area;

    // Core blob analysis arrays
    int blob_bound[3][max_num_blobs];
    int blob_area[max_num_blobs];
    double blob_eccentricity[max_num_blobs];
    double blob_centroid[2][max_num_blobs];
    double bounding_box[4][max_num_blobs];
    double major_axis[max_num_blobs];
    double minor_axis[max_num_blobs];
    double blob_probability[max_num_blobs];
    bool is_dot[max_num_blobs];

    // Post process logic variables
    int blob_dist_thresh;  // Static migration distance threshold

    // Position field logic variables
    int area_thresh;  // Global thresholds for dot identification
    int num_dots;
    double migration_dist_thresh;  // Dynamic migration distance threshold

    // Position field data
    double positionField_ref[2][max_num_blobs];  // Reference position field; retained in memory for easy comparison with current field
    double positionField[2][max_num_blobs];  // Position field of current image

    // Variables used for generateGridMap()
    double min_direction_angle;  // Units are radians
    double max_direction_angle;
    double max_neighbor_dist;
    int gridMap[max_neighbor_count][max_num_blobs];

    // Strain field
    double epsxx[max_num_blobs];  // Four arrays in which strain data is stored for current time step
    double epsxy[max_num_blobs];
    double epsyx[max_num_blobs];
    double epsyy[max_num_blobs];

    // Variables used in strain calculation
    double epsCollection[4][calculation_count];  // Temporary structure for strain calculation

    // Parameters tracked between images
    double globalMinStrain, globalMaxStrain;

    //tmp
    int t;
    double dotProbabilityThresh;

};


#endif // CORRELATION_H
